﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace System
{
	public static class TypeExtensions
	{
		/// <summary>
		/// Indica se o tipo <paramref name="t"/> implementa a interface "IQueryable"
		/// </summary>
		/// <param name="t"></param>
		/// <returns></returns>
		public static bool IsQueryable( this Type t )
		{
			return ( t != null
				&& typeof( IQueryable ).IsAssignableFrom( t ) )
				|| t == typeof( Queryable );
		}

		/// <summary>
		/// Indica se o tipo <paramref name="t"/> implementa a interface <see cref="System.Collections.Generic.ICollection"/>
		/// </summary>
		/// <param name="t"></param>
		/// <returns></returns>
		public static bool IsCollection( this Type t )
		{
			Type itemType;
			return t.IsCollection( out itemType );
		}
		
		/// <summary>
		/// Indica se o tipo <paramref name="t"/> implementa a interface <see cref="System.Collections.Generic.ICollection"/>
		/// e retorna o tipo do item da coleção
		/// </summary>
		/// <param name="t"></param>
		/// <param name="itemType"></param>
		/// <returns></returns>
		public static bool IsCollection( this Type t, out Type itemType )
		{
			Type current = t;
			Type collectionType = typeof( ICollection<> );

			itemType = ( from ti in t.GetInterfaces()
						 where ti.IsGenericType
						  	&& ti.GetGenericTypeDefinition().Equals( collectionType )
						 select ti.GetGenericArguments()[ 0 ] ).FirstOrDefault();

			return itemType != null;
		}

		/// <summary>
		/// Indica se o tipo <paramref name="t"/> implementa a interface "IEnumerable"
		/// </summary>
		/// <param name="t"></param>
		/// <returns></returns>
		public static bool IsEnumerable( this Type t )
		{
			return t != null && typeof( global::System.Collections.IEnumerable ).IsAssignableFrom( t );
		}

		/// <summary>
		/// Indica se <paramref name="T"/> implementa a interface <typeparamref name="T"/>
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="t"></param>
		/// <returns></returns>
		public static bool Implements<T>( this Type t )
		{
			return t != null && typeof( T ).IsAssignableFrom( t );
		}


		/// <summary>
		/// 
		/// </summary>
		/// <param name="baseType"></param>
		/// <returns></returns>
		public static bool InheritedFrom( this Type who, Type parent )
		{
			if ( who != null && parent != null )
			{
				return who.BaseType.IsOrInheritFrom( parent );
			}

			return false;
		}


		public static bool IsOrInheritFrom( this Type who, Type parent )
		{
			if ( who != null && parent != null )
			{
				Type current = who;

				while ( current != null )
				{
					if ( current.Equals( parent ) )
						return true;
					current = current.BaseType;
				}
			}

			return false;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="t"></param>
		/// <param name="methodName"></param>
		/// <param name="parameters"></param>
		public static MethodCaller Call( this Type t, string methodName, params object[] parameters )
		{
			return new MethodCaller( t.GetMethods().First( method => method.Name == methodName ), parameters );
		}

		public static PropertyInfo GetProperty( this Type t, string name )
		{
			return t.GetProperty( name, BindingFlags.Public | BindingFlags.Instance );
		}
	}

}
